`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2020/10/22 17:33:53
// Design Name: 
// Module Name: data_mem
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module data_mem(
    input logic  [ 3: 0]            store_sel,
    input logic  [ 3: 0]            load_type, 
                                          
    input logic                     we_i,               
    input logic  [31: 0]            adr_i,            
    input logic  [31: 0]            write_data_i,     
    input logic  [31: 0]            read_data_i,     

    output logic                    en,
    output logic [ 3: 0]            we_o,
    output logic [31: 0]            adr_o,
    output logic [31: 0]            write_data_o,
    output logic [31: 0]            read_data_o,
    
    input  logic [`EXCS_BUS]        excs_i,
    output logic [`EXCS_BUS]        excs_o
    ); 

    logic badadr_flag_l;
    logic badadr_flag_s;
    always_comb begin
        excs_o              = excs_i;
        excs_o[`EXC_D_ADE]  = badadr_flag_s | badadr_flag_l;
    end 

    assign en = 1'b1;

    logic [31:0] rd;
    assign rd    = read_data_i;
    assign adr_o = adr_i;
    //load
    always_comb begin
        read_data_o = 32'b0;   
        badadr_flag_l = 1'b0;     
        case(load_type)
            4'b0000:    begin   //lb
                case(adr_i[1:0])
                    2'b00:  begin
                        read_data_o = {{24{rd[7]}},rd[7:0]};
                        badadr_flag_l = 1'b0;
                    end
                    2'b01:  begin
                        read_data_o = {{24{rd[15]}},rd[15:8]};
                        badadr_flag_l = 1'b0;
                    end
                    2'b10:  begin
                        read_data_o = {{24{rd[23]}},rd[23:16]};
                        badadr_flag_l = 1'b0;
                    end
                    2'b11:  begin
                        read_data_o = {{24{rd[31]}},rd[31:24]};
                        badadr_flag_l = 1'b0;
                    end
                endcase
            end
            4'b0001:    begin   //lbu
                case(adr_i[1:0])
                    2'b00:  begin
                        read_data_o = {24'b0,rd[7:0]};
                        badadr_flag_l = 1'b0;
                    end
                    2'b01:  begin
                        read_data_o = {24'b0,rd[15:8]};
                        badadr_flag_l = 1'b0;
                    end
                    2'b10:  begin
                        read_data_o = {24'b0,rd[23:16]};
                        badadr_flag_l = 1'b0;
                    end
                    2'b11:  begin
                        read_data_o = {24'b0,rd[31:24]};
                        badadr_flag_l = 1'b0;
                    end
                endcase
            end
            4'b0010:    begin   //lh
                case(adr_i[1:0])
                    2'b00:  begin
                        read_data_o = {{16{rd[15]}},rd[15:0]};
                        badadr_flag_l = 1'b0;
                    end
                    2'b01:  begin
                        badadr_flag_l = 1'b1;
                    end
                    2'b10:  begin
                        read_data_o = {{16{rd[31]}},rd[31:16]};
                        badadr_flag_l = 1'b0;
                    end
                    2'b11:  begin
                        badadr_flag_l = 1'b1;
                    end
                endcase
            end
            4'b0011:    begin   //lhu
                case(adr_i[1:0])
                    2'b00:  begin
                        read_data_o = {16'b0,rd[15:0]};
                        badadr_flag_l = 1'b0;
                    end
                    2'b01:  begin
                        badadr_flag_l = 1'b1;
                    end
                    2'b10:  begin
                        read_data_o = {16'b0,rd[31:16]};
                        badadr_flag_l = 1'b0;
                    end
                    2'b11:  begin
                        badadr_flag_l = 1'b1;
                    end
                endcase
            end  
            4'b0100:    begin   //lw
                case(adr_i[1:0])
                    2'b00:  begin
                        read_data_o = rd;
                        badadr_flag_l = 1'b0;
                    end
                    2'b01:  begin
                        badadr_flag_l = 1'b1;
                    end
                    2'b10:  begin
                        badadr_flag_l = 1'b1;
                    end
                    2'b11:  begin
                        badadr_flag_l = 1'b1;
                    end
                endcase
            end
        endcase
    end

    //store
    always_comb   begin
        we_o = 4'b0000;
        write_data_o = 32'b0;
        badadr_flag_s = 1'b0;
         if(we_i)  begin
            case(store_sel)
                4'b0001:    begin
                    write_data_o = {4{write_data_i[7:0]}};
                    case(adr_i[1:0])  //sb
                        2'b00:  begin
                            we_o = 4'b0001;
                            badadr_flag_s = 1'b0;
                        end
                        2'b01:  begin
                            we_o = 4'b0010;
                            badadr_flag_s = 1'b0;
                        end
                        2'b10:  begin
                            we_o = 4'b0100;
                            badadr_flag_s = 1'b0;
                        end
                        2'b11:  begin
                            we_o = 4'b1000;
                            badadr_flag_s = 1'b0;
                        end
                    endcase
                end
                4'b0011:    begin
                    write_data_o = {2{write_data_i[15:0]}};
                    case(adr_i[1:0])  //sh
                        2'b00:  begin
                            we_o = 4'b0011;
                            badadr_flag_s = 1'b0;
                        end
                        2'b01:  begin
                            badadr_flag_s = 1'b1;
                        end
                        2'b10:  begin
                            we_o = 4'b1100;
                            badadr_flag_s = 1'b0;
                        end
                        2'b11:  begin
                            badadr_flag_s = 1'b1;
                        end
                    endcase
                end
                4'b1111:    begin
                     write_data_o = write_data_i;
                    case(adr_i[1:0])  //sw
                        2'b00:  begin
                            we_o = 4'b1111;
                            badadr_flag_s = 1'b0;
                        end
                        2'b01:  begin
                            badadr_flag_s = 1'b1;
                        end
                        2'b10:  begin
                            badadr_flag_s = 1'b1;
                        end
                        2'b11:  begin
                            badadr_flag_s = 1'b1;
                        end
                    endcase
                end
                endcase
        end
    end

endmodule
